//===--- ArgsToFrontendOutputsConverter.h -----------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_FRONTEND_ARGSTOFRONTENDOUTPUTSCONVERTER_H
#define POLARPHP_FRONTEND_ARGSTOFRONTENDOUTPUTSCONVERTER_H

#include "polarphp/ast/DiagnosticConsumer.h"
#include "polarphp/ast/DiagnosticEngine.h"
#include "polarphp/basic/LLVM.h"
#include "polarphp/basic/SupplementaryOutputPaths.h"
#include "polarphp/frontend/FrontendOptions.h"
#include "polarphp/option/Options.h"
#include "llvm/Option/ArgList.h"

#include <vector>

namespace polar {
class OutputFileMap;

/// Given the command line arguments and information about the inputs,
/// Fill in all the information in FrontendInputsAndOutputs.

class ArgsToFrontendOutputsConverter {
   const llvm::opt::ArgList &Args;
   StringRef ModuleName;
   FrontendInputsAndOutputs &InputsAndOutputs;
   DiagnosticEngine &Diags;

public:
   ArgsToFrontendOutputsConverter(const llvm::opt::ArgList &args,
                                  StringRef moduleName,
                                  FrontendInputsAndOutputs &inputsAndOutputs,
                                  DiagnosticEngine &diags)
      : Args(args), ModuleName(moduleName), InputsAndOutputs(inputsAndOutputs),
        Diags(diags) {}

   bool convert(std::vector<std::string> &mainOutputs,
                std::vector<SupplementaryOutputPaths> &supplementaryOutputs);

   /// Try to read an output file list file.
   /// \returns `None` if it could not open the filelist.
   static Optional<std::vector<std::string>>
   readOutputFileList(StringRef filelistPath, DiagnosticEngine &diags);
};

class OutputFilesComputer {
   DiagnosticEngine &Diags;
   const FrontendInputsAndOutputs &InputsAndOutputs;
   const std::vector<std::string> OutputFileArguments;
   const std::string OutputDirectoryArgument;
   const std::string FirstInput;
   const FrontendOptions::ActionType RequestedAction;
   const llvm::opt::Arg *const ModuleNameArg;
   const StringRef Suffix;
   const bool HasTextualOutput;

   OutputFilesComputer(DiagnosticEngine &diags,
                       const FrontendInputsAndOutputs &inputsAndOutputs,
                       std::vector<std::string> outputFileArguments,
                       StringRef outputDirectoryArgument, StringRef firstInput,
                       FrontendOptions::ActionType requestedAction,
                       const llvm::opt::Arg *moduleNameArg, StringRef suffix,
                       bool hasTextualOutput);

public:
   static Optional<OutputFilesComputer>
   create(const llvm::opt::ArgList &args, DiagnosticEngine &diags,
          const FrontendInputsAndOutputs &inputsAndOutputs);

   /// \return the output filenames on the command line or in the output
   /// filelist. If there
   /// were neither -o's nor an output filelist, returns an empty vector.
   static Optional<std::vector<std::string>>
   getOutputFilenamesFromCommandLineOrFilelist(const llvm::opt::ArgList &args,
                                               DiagnosticEngine &diags);

   Optional<std::vector<std::string>> computeOutputFiles() const;

private:
   Optional<std::string> computeOutputFile(StringRef outputArg,
                                           const InputFile &input) const;

   /// \return the correct output filename when none was specified.
   ///
   /// Such an absence should only occur when invoking the frontend
   /// without the driver,
   /// because the driver will always pass -o with an appropriate filename
   /// if output is required for the requested action.
   Optional<std::string> deriveOutputFileFromInput(const InputFile &input) const;

   /// \return the correct output filename when a directory was specified.
   ///
   /// Such a specification should only occur when invoking the frontend
   /// directly, because the driver will always pass -o with an appropriate
   /// filename if output is required for the requested action.
   Optional<std::string>
   deriveOutputFileForDirectory(const InputFile &input) const;

   std::string determineBaseNameOfOutput(const InputFile &input) const;

   std::string deriveOutputFileFromParts(StringRef dir, StringRef base) const;
};

class SupplementaryOutputPathsComputer {
   const llvm::opt::ArgList &Args;
   DiagnosticEngine &Diags;
   const FrontendInputsAndOutputs &InputsAndOutputs;
   ArrayRef<std::string> OutputFiles;
   StringRef ModuleName;

   const FrontendOptions::ActionType RequestedAction;

public:
   SupplementaryOutputPathsComputer(
      const llvm::opt::ArgList &args, DiagnosticEngine &diags,
      const FrontendInputsAndOutputs &inputsAndOutputs,
      ArrayRef<std::string> outputFiles, StringRef moduleName);
   Optional<std::vector<SupplementaryOutputPaths>> computeOutputPaths() const;

private:
   /// \Return a set of supplementary output paths for each input that might
   /// produce supplementary outputs, or None to signal an error.
   /// \note
   /// Batch-mode supports multiple primary inputs.
   /// \par
   /// The paths are derived from arguments
   /// such as -emit-module-path. These are not the final computed paths,
   /// merely the ones passed in via the command line.
   /// \note
   /// In the future, these will also include those passed in via whatever
   /// filelist scheme gets implemented to handle cases where the command line
   /// arguments become burdensome.
   Optional<std::vector<SupplementaryOutputPaths>>
   getSupplementaryOutputPathsFromArguments() const;

   /// Read a supplementary output file map file.
   /// \returns `None` if it could not open the file map.
   Optional<std::vector<SupplementaryOutputPaths>>
   readSupplementaryOutputFileMap() const;

   /// Given an ID corresponding to supplementary output argument
   /// (e.g. -emit-module-path), collect all such paths, and ensure
   /// there are the right number of them.
   Optional<std::vector<std::string>>
   getSupplementaryFilenamesFromArguments(options::FileTypeId pathID) const;

   Optional<SupplementaryOutputPaths> computeOutputPathsForOneInput(
      StringRef outputFilename,
      const SupplementaryOutputPaths &pathsFromFilelists,
      const InputFile &) const;

   StringRef deriveDefaultSupplementaryOutputPathExcludingExtension(
      StringRef outputFilename, const InputFile &) const;

   /// \return empty string if no output file.
   std::string determineSupplementaryOutputFilename(
      options::FileTypeId emitOpt, std::string pathFromArgumentsOrFilelists,
      filetypes::FileTypeId type, StringRef mainOutputIfUsable,
      StringRef defaultSupplementaryOutputPathExcludingExtension) const;

   void deriveModulePathParameters(StringRef mainOutputFile,
                                   options::FileTypeId &emitOption,
                                   std::string &extension,
                                   std::string &mainOutputIfUsable) const;
};

} // namespace polar

#endif /* POLARPHP_FRONTEND_ARGSTOFRONTENDOUTPUTSCONVERTER_H */
